﻿using System;
using System.Net;
using System.Net.Sockets;
using System.Text;
using System.Threading;

namespace SocketUtil
{
    public class SocketNode
    {
        private Poller Poller;

        public SocketNode(Socket socket)
        {
            Socket = socket;
            Poller = new Poller("SocketNode");
            Poller.Work = Work;
            if (Socket == null)
            {
                Socket = new Socket(AddressFamily.InterNetwork, SocketType.Dgram, ProtocolType.Udp);
            }
            AddressFamily = Socket.AddressFamily;
            SocketType = Socket.SocketType;
            ProtocolType = Socket.ProtocolType;
        }

        public SocketNode()
            : this(new Socket(AddressFamily.InterNetwork, SocketType.Dgram, ProtocolType.Udp))
        {
        }

        public EndPoint LocalPoint { get; set; }
        public EndPoint RemotePoint { get; set; }
        public Socket Socket { get; set; }
        private AddressFamily AddressFamily { get; set; }
        private bool Enable { get; set; }
        private ProtocolType ProtocolType { get; set; }
        private SocketType SocketType { get; set; }
        public void BindLocal(string host = null, int port = 0)
        {
            if (!string.IsNullOrEmpty(host))
                LocalPoint = new IPEndPoint(IPAddress.Any, port);
            if (!Enable)
                return;
            if (Socket != null)
            {
                Socket.Bind(LocalPoint);
            }
        }

        public void Connect(string host, int port)
        {
            lock (this)
            {
                try
                {
                    if (Socket != null && Socket.Connected)
                    {
                        Socket.Shutdown(SocketShutdown.Both);
                        Thread.Sleep(20);
                        Socket.Close();
                    }
                    RemotePoint = new IPEndPoint(IPAddress.Parse(host), port);
                    Socket = new Socket(AddressFamily, SocketType, ProtocolType);
                    Socket.Connect(RemotePoint);
                    Enable = true;
                }
                catch (Exception ex)
                {
                    System.Diagnostics.Trace.TraceError(ex.Message);
                    System.Diagnostics.Trace.TraceError(ex.StackTrace);
                    System.Diagnostics.Trace.TraceError(ex.Source);
                }
            }
        }

        public void Disconnect()
        {
            StopListen();
            lock (this)
            {
                try
                {
                    if (Socket != null)
                    {
                        Socket.Shutdown(SocketShutdown.Both);
                        Thread.Sleep(20);
                    }
                }
                catch (Exception ex)
                {
                    System.Diagnostics.Trace.TraceError(ex.Message);
                    System.Diagnostics.Trace.TraceError(ex.StackTrace);
                    System.Diagnostics.Trace.TraceError(ex.Source);
                }
                finally
                {
                    Socket.Close();
                    Socket = null;
                    GC.Collect();
                }
                Enable = false;
            }
        }
        public void Reconnect()
        {
            try
            {
                lock (this)
                {
                    if (Socket == null)
                    {
                        Socket = new Socket(AddressFamily, SocketType, ProtocolType);
                        if (LocalPoint != null)
                            Socket.Bind(LocalPoint);
                    }
                    if (!Socket.Connected)
                    {
                        Socket.Connect(RemotePoint);
                        Enable = true;
                    }
                }
            }
            catch (Exception ex)
            {
                System.Diagnostics.Trace.TraceError(ex.Message);
                System.Diagnostics.Trace.TraceError(ex.StackTrace);
                System.Diagnostics.Trace.TraceError(ex.Source);
            }
        }

        #region 数据轮询监听

        public event Action<byte[], int> OnData;

        public void StartListen()
        {
            if (!Socket.Connected)
                Reconnect();
            Poller.Continue();
        }

        public void StopListen()
        {
            Poller.Pause();
        }

        private void Work()
        {
            if (!Enable)
                return;
            try
            {
                if (Socket.Available <= 0)
                {
                    return;
                }
                var buff = new byte[Socket.Available];
                int x = Socket.Receive(buff, 0, Socket.Available, SocketFlags.None);
                System.Diagnostics.Trace.WriteLine($"Socket:\t{x}");
                if (x > 0)
                {
                    OnData?.Invoke(buff, x);
                }
            }
            catch (SocketException ex)
            {
                System.Diagnostics.Trace.TraceError(ex.Message);
                System.Diagnostics.Trace.TraceError(ex.StackTrace);
                System.Diagnostics.Trace.TraceError(ex.Source);
            }
            catch (Exception ex)
            {
                System.Diagnostics.Trace.TraceError(ex.Message);
                System.Diagnostics.Trace.TraceError(ex.StackTrace);
                System.Diagnostics.Trace.TraceError(ex.Source);
            }
        }

        #endregion 数据轮询监听

        #region 接收与发送

        public int Receive(out string str)
        {
            str = null;
            try
            {
                var buff = new byte[Socket.Available];
                int x = Socket.Receive(buff, 0, Socket.Available, SocketFlags.None);
                if (x > 0)
                {
                    str = Encoding.UTF8.GetString(buff);
                }
                return x;
            }
            catch (SocketException ex)
            {
                System.Diagnostics.Trace.TraceError(ex.Message);
                System.Diagnostics.Trace.TraceError(ex.StackTrace);
                System.Diagnostics.Trace.TraceError(ex.Source);
                throw ex;
            }
            catch (Exception ex)
            {
                System.Diagnostics.Trace.TraceError(ex.Message);
                System.Diagnostics.Trace.TraceError(ex.StackTrace);
                System.Diagnostics.Trace.TraceError(ex.Source);
                throw ex;
            }
        }

        public int Receive(out byte[] buff)
        {
            buff = null;
            try
            {
                buff = new byte[Socket.Available];
                int x = Socket.Receive(buff, 0, Socket.Available, SocketFlags.None);
                return x;
            }
            catch (SocketException ex)
            {
                System.Diagnostics.Trace.TraceError(ex.Message);
                System.Diagnostics.Trace.TraceError(ex.StackTrace);
                System.Diagnostics.Trace.TraceError(ex.Source);
                throw ex;
            }
            catch (Exception ex)
            {
                System.Diagnostics.Trace.TraceError(ex.Message);
                System.Diagnostics.Trace.TraceError(ex.StackTrace);
                System.Diagnostics.Trace.TraceError(ex.Source);
                throw ex;
            }
        }

        public void ReceiveAsync(EventHandler<SocketAsyncEventArgs> Completed)
        {
            try
            {
                var args = new SocketAsyncEventArgs();
                args.SocketFlags = SocketFlags.None;
                args.Completed += Completed;
                Socket.ReceiveAsync(args);
            }
            catch (SocketException ex)
            {
                System.Diagnostics.Trace.TraceError(ex.Message);
                System.Diagnostics.Trace.TraceError(ex.StackTrace);
                System.Diagnostics.Trace.TraceError(ex.Source);
                throw ex;
            }
            catch (Exception ex)
            {
                System.Diagnostics.Trace.TraceError(ex.Message);
                System.Diagnostics.Trace.TraceError(ex.StackTrace);
                System.Diagnostics.Trace.TraceError(ex.Source);
                throw ex;
            }
        }

        public int Send(string str)
        {
            try
            {
                byte[] msg = Encoding.UTF8.GetBytes(str);
                int byteCount = Socket.Send(msg, 0, msg.Length, SocketFlags.None);
                return byteCount;
            }
            catch (SocketException ex)
            {
                System.Diagnostics.Trace.TraceError(ex.Message);
                System.Diagnostics.Trace.TraceError(ex.StackTrace);
                System.Diagnostics.Trace.TraceError(ex.Source);
                throw ex;
            }
            catch (Exception ex)
            {
                System.Diagnostics.Trace.TraceError(ex.Message);
                System.Diagnostics.Trace.TraceError(ex.StackTrace);
                System.Diagnostics.Trace.TraceError(ex.Source);
                throw ex;
            }
        }

        public int Send(byte[] buff, int offset, int count)
        {
            try
            {
                int byteCount = Socket.Send(buff, offset, count, SocketFlags.None);
                return byteCount;
            }
            catch (SocketException ex)
            {
                System.Diagnostics.Trace.TraceError(ex.Message);
                System.Diagnostics.Trace.TraceError(ex.StackTrace);
                System.Diagnostics.Trace.TraceError(ex.Source);
                throw ex;
            }
            catch (Exception ex)
            {
                System.Diagnostics.Trace.TraceError(ex.Message);
                System.Diagnostics.Trace.TraceError(ex.StackTrace);
                System.Diagnostics.Trace.TraceError(ex.Source);
                throw ex;
            }
        }

        public void SendAsync(byte[] buff, int offset, int count, EventHandler<SocketAsyncEventArgs> Completed)
        {
            try
            {
                var args = new SocketAsyncEventArgs();
                args.SocketFlags = SocketFlags.None;
                args.SetBuffer(buff, offset, count);
                args.Completed += Completed;
                Socket.SendAsync(args);
            }
            catch (SocketException ex)
            {
                System.Diagnostics.Trace.TraceError(ex.Message);
                System.Diagnostics.Trace.TraceError(ex.StackTrace);
                System.Diagnostics.Trace.TraceError(ex.Source);
                throw ex;
            }
            catch (Exception ex)
            {
                System.Diagnostics.Trace.TraceError(ex.Message);
                System.Diagnostics.Trace.TraceError(ex.StackTrace);
                System.Diagnostics.Trace.TraceError(ex.Source);
                throw ex;
            }
        }

        public void SendAsync(string str, EventHandler<SocketAsyncEventArgs> Completed)
        {
            try
            {
                byte[] msg = Encoding.UTF8.GetBytes(str);
                var args = new SocketAsyncEventArgs();
                args.SocketFlags = SocketFlags.None;
                args.SetBuffer(msg, 0, msg.Length);
                args.Completed += Completed;
                Socket.SendAsync(args);
            }
            catch (SocketException ex)
            {
                System.Diagnostics.Trace.TraceError(ex.Message);
                System.Diagnostics.Trace.TraceError(ex.StackTrace);
                System.Diagnostics.Trace.TraceError(ex.Source);
                throw ex;
            }
            catch (Exception ex)
            {
                System.Diagnostics.Trace.TraceError(ex.Message);
                System.Diagnostics.Trace.TraceError(ex.StackTrace);
                System.Diagnostics.Trace.TraceError(ex.Source);
                throw ex;
            }
        }

        #endregion 接收与发送
    }
}